﻿using System;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;

namespace O1.Data
{
    [ContractVerification(true)]
    [StructLayout(LayoutKind.Explicit)]
    public struct Edge : IEquatable<Edge>
    {
        public const int Size = Sz.Cell.Edge;

        [FieldOffset(0)]
        [CLSCompliant(false)]
        public readonly uint EndCellId;

        [FieldOffset(Sz.CLR.Int32)]
        [CLSCompliant(false)]
        public readonly ushort EdgeType;

        [FieldOffset(Sz.CLR.Int32 + Sz.CLR.Int16)]
        [CLSCompliant(false)]
        public readonly ushort EndTrunkId;

        [CLSCompliant(false)]
        public Edge(ushort edgeType, uint endCellId, ushort endTrunkId)
            : this()
        {
            this.EdgeType = edgeType;
            this.EndCellId = endCellId;
            this.EndTrunkId = endTrunkId;
        }

        public static implicit operator CellAddress(Edge value)
        {
            return new CellAddress(value.EndTrunkId, value.EndCellId);
        }

        public static bool operator ==(Edge instance, Edge other)
        {
            return instance.Equals(other);
        }

        public static bool operator !=(Edge instance, Edge other)
        {
            return !instance.Equals(other);
        }

        public static implicit operator long(Edge value)
        {
            unsafe
            {
                return *(long*)&value;
            }
        }

        public static implicit operator Edge(long value)
        {
            unsafe
            {
                return *(Edge*)&value;
            }
        }

        public bool Equals(Edge other)
        {
            return
                this.EdgeType == other.EdgeType &&
                this.EndCellId == other.EndCellId &&
                this.EndTrunkId == other.EndTrunkId;
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }

            if (obj.GetType() != typeof(Edge))
            {
                return false;
            }

            return this.Equals((Edge)obj);
        }

        public override int GetHashCode()
        {
            return (this.EndTrunkId.GetHashCode() ^ this.EdgeType.GetHashCode() ^ this.EndCellId).GetHashCode();
        }

        public CellAddress ToRemoteCellAddress()
        {
            return (CellAddress)this;
        }

        internal static unsafe Edge Read(byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Size < buffer.Length, ExceptionText.ValueBufferTooSmall);
            
            fixed (byte* p = &buffer[offset])
            {
                return *(Edge*)p;
            }
        }
        
        internal unsafe void Write(byte[] buffer, int offset)
        {
            Contract.Requires(buffer != null);
            Contract.Requires(0 <= offset);
            Contract.Requires(offset + Size < buffer.Length, ExceptionText.ValueBufferTooSmall);
            
            fixed (byte* p = &buffer[offset])
            {
                *((Edge*)p) = this;
            }
        }
    }
}